;(function ($) {
$.fn.jqCrossTable = function( p ) {
	p = $.extend(true,{
		rows:[],
		cols:[],
		detail:[],
		datastr:'[]',
		datatype: "jsonstring",
		jsonReader: {root:'data'},
		showDetailCaption: true
	}, p || {});
	return this.each(function(){
		if(this.cross) {return;}
		this.p = p;
		var ct = this;
		ct.cross = {};
		ct.cross.lefttopdiv = $("<div class='ctlefttop'/>").appendTo(ct);
		ct.cross.leftdiv = $("<div class='ctleft'/>").appendTo(ct);
		ct.cross.topdiv = $("<div class='cttop'/>").appendTo(ct);
		ct.cross.contentdiv = $("<div class='ctcontent'/>").appendTo(ct).scroll(function(ev){
			ct.cross.topdiv.scrollLeft(ct.cross.contentdiv.scrollLeft());
			ct.cross.leftdiv.scrollTop(ct.cross.contentdiv.scrollTop());
		});
		ct.data = eval('('+ct.p.datastr+')');
		ct.DimRowHeaders = {values:{},count:0};
		ct.DimColHeaders = {values:{},count:0};
		ct.DimRowCount = 0;
		ct.DimColCount = 0;
		ct.p.rowdetailcount = 0;
		ct.p.coldetailcount = 0;
		var grrows = {rowcount:-1};
		var grcols = {colcount:-1};
		$.each(ct.p.rows,function(rindex, r){
			r = $.extend(true,{rowdetail:[]},r);
			ct.p.rowdetailcount = Math.max(ct.p.rowdetailcount,r.rowdetail.length);
		});
		$.each(ct.p.cols,function(rindex, r){
			r = $.extend(true,{coldetail:[]},r);
			ct.p.coldetailcount = Math.max(ct.p.coldetailcount,r.coldetail.length);
		});
		$.each(ct.p.detail,function(rindex, r){
			r = $.extend(true,{cellclass:''},r);
		});
		ct.cross.lefttopdiv.css({height:(ct.p.cols.length+ct.p.coldetailcount+((ct.p.showDetailCaption === true || ct.p.cols.length==0)?1:0))*20+'px'})
		ct.cross.leftdiv.css({top:(ct.p.cols.length+ct.p.coldetailcount+((ct.p.showDetailCaption === true || ct.p.cols.length==0)?1:0))*20+'px'})
		ct.cross.topdiv.css({height:(ct.p.cols.length+ct.p.coldetailcount+((ct.p.showDetailCaption === true || ct.p.cols.length==0)?1:0))*20+'px'})
		ct.cross.contentdiv.css({top:(ct.p.cols.length+ct.p.coldetailcount+((ct.p.showDetailCaption === true || ct.p.cols.length==0)?1:0))*20+'px'})
		if (ct.data[ct.p.jsonReader.root].length>0)
		{
			//Строки
			var $leftTable = $('<table class="ui-widget"><tbody></tbody></table>');
			var $topTable = $('<table class="ui-widget"><tbody></tbody></table>');
			var $leftTableBody = $('tbody',$leftTable);
			var $topTableBody = $('tbody',$topTable);
			var $contentTable = $('<table class="ui-widget"><tbody></tbody></table>');
			var $contentTableBody = $('tbody',$contentTable);
			var prevvalues = [];
			var dimspancount = [];
			function goDeep(data){
				$.each(data,function(datarowidx, datarow){
					function getGroupsRowId(r, groupLevel){
						var hashrowvalue = '';
						for (var j=0;j<groupLevel;j++){
							hashrowvalue += '$$$'+typeof ct.p.rows[j].calculated == 'function'?ct.p.rows[j].calculated(r):r[ct.p.rows[j].name];
						}
						if (typeof grrows[hashrowvalue] == 'undefined')
						{
							grrows['rowcount']++;
							grrows[hashrowvalue] = grrows['rowcount'];
							grrows[grrows[hashrowvalue]] = [];
							for (var j=0;j<groupLevel;j++){
								grrows[grrows[hashrowvalue]][j] = typeof ct.p.rows[j].calculated == 'function'?ct.p.rows[j].calculated(r):r[ct.p.rows[j].name];
							}
						}
						return grrows[hashrowvalue];
					}
					function getGroupsColId(r, groupLevel){
						var hashcolvalue = '';
						for (var j=0;j<groupLevel;j++){
							if (j<ct.p.cols.length)
							{
								hashcolvalue += '$$$'+typeof ct.p.cols[j].calculated == 'function'?ct.p.cols[j].calculated(r):r[ct.p.cols[j].name];
							}
							else
							{
								hashcolvalue += ct.p.detail[0].caption;//ct.p.detail[0].caption;
							}
						}
						if (typeof grcols[hashcolvalue] == 'undefined')
						{
							grcols['colcount']++;
							grcols[hashcolvalue] = grcols['colcount'];
							grcols[grcols[hashcolvalue]] = [];
							for (var j=0;j<groupLevel;j++){
								if (j<ct.p.cols.length)
								{
									grcols[grcols[hashcolvalue]][j] = typeof ct.p.cols[j].calculated == 'function'?ct.p.cols[j].calculated(r):r[ct.p.cols[j].name];
								}
								else
								{
									grcols[grcols[hashcolvalue]][j] = ct.p.detail[0].caption;
								}
							}
						}
						return grcols[hashcolvalue];
					}
					var $contenttr;
					var $detailcaptiontr = $($topTableBody[0].childNodes[$topTableBody[0].childNodes.length-1]);
					var colwidth = 100;
					var colgroupwidth = (ct.p.cols.length==0)?colwidth:colwidth+3*(ct.p.detail.length-1);
					var coldetailwidth = (ct.p.cols.length==0)?colwidth:colwidth/ct.p.detail.length;
					var detailvalues = [];
					for (var j=0;j<ct.p.detail.length;j++)
					{
						detailvalues[j]=ct.p.detail[j].value(datarow);
					}
					if (ct.p.cols.length>0)
					{
						for (var j=0;j<ct.p.cols.length;j++)
						{
							var colId = getGroupsColId(datarow, j+1);
							var $tr = $($topTableBody[0].childNodes[j]);
							if ($tr[0].childElementCount<=colId)
							{
								$tr.append('<td style="width:'+colgroupwidth+'px" colspan="'+ct.p.detail.length*(j+1)+'" class="ui-widget-content ui-state-default">'+grcols[colId][j]+'</td>');
								if (ct.p.showDetailCaption === true && j==ct.p.cols.length-1)
								{
									for (var m=0;m<ct.p.detail.length;m++)
									{
										$detailcaptiontr.append('<td style="width:'+colgroupwidth+'px" class="ui-widget-content ui-state-default">'+ct.p.detail[m].caption+'</td>');
									}
								}
							}
							for (var coldetailid = 0;coldetailid<ct.p.cols[j].coldetail.length;coldetailid++)
							{
								var $detailtr = $($topTableBody[0].childNodes[ct.p.cols.length+coldetailid]);
								if ($detailtr[0].childElementCount<=colId)
								{
									$detailtr.append('<td style="width:'+colgroupwidth+'px" colspan="'+ct.p.detail.length*(j+1)+'" class="ui-widget-content ui-state-default '+(ct.p.cols[j].coldetail[coldetailid].cellclass || '')+'">'+((typeof ct.p.cols[j].coldetail[coldetailid]['calculated'] == 'function')?ct.p.cols[j].coldetail[coldetailid]['calculated'](datarow):datarow[ct.p.cols[j].coldetail[coldetailid].name])+'</td>');
								}
							}
						}
					}
					else
					{
						colId = 0;
						if ($detailcaptiontr[0].childNodes.length<ct.p.detail.length)
						{
							for (var m=0;m<ct.p.detail.length;m++)
							{
								$detailcaptiontr.append('<td style="width:'+colwidth+'px" class="ui-widget-content ui-state-default">'+ct.p.detail[m].caption+'</td>');
							}
						}
					}
					for (var j=0;j<ct.p.rows.length;j++)
					{
						var rowId = getGroupsRowId(datarow, j+1);
						if ($leftTableBody[0].childElementCount<=rowId)
						{
							var $tr = $('<tr/>').appendTo($leftTableBody);
							for (var k=0;k<grrows[rowId].length;k++)
							{
								if (k<j)
								{
									$tr.append('<td>&nbsp;</td>');
								}
								else
								{
									$tr.append('<td colspan="'+(ct.p.rows.length-j+ct.p.rowdetailcount-ct.p.rows[j].rowdetail.length)+'" class="ui-widget-header ui-state-default">'+grrows[rowId][k]+'</td>');
									for (var m=0;m<ct.p.rows[j].rowdetail.length;m++)
									{
										$tr.append('<td class="ui-widget-content '+(ct.p.rows[j].rowdetail[m].cellclass || '')+'">'+((typeof ct.p.rows[j].rowdetail[m]['calculated'] == 'function')?ct.p.rows[j].rowdetail[m]['calculated'](datarow):datarow[ct.p.rows[j].rowdetail[m].name])+'</td>');
									}
								}
							}
							$contenttr = $('<tr/>').appendTo($contentTableBody);
						}
						else
						{
							$contenttr = $($contentTableBody[0].childNodes[rowId]);
						}
						for (var k=$contenttr[0].childElementCount;k<=(colId+1)*ct.p.detail.length-1;k++)
						{
							$contenttr.append('<td style="width:'+coldetailwidth+'px" class="ui-widget-content">&nbsp;</td>');
						}
					}
					for (var j=0;j<ct.p.detail.length;j++)
					{
						$($contenttr[0].childNodes[colId*ct.p.detail.length+j]).addClass(ct.p.detail[j]['cellclass']).html(detailvalues[j]);
					}
				});
			}
			for (var colid = 0;colid<ct.p.cols.length+((ct.p.showDetailCaption === true || ct.p.cols.length==0)?1:0);colid++)
			{
				$topTableBody.append('<tr/>');
				if (colid<ct.p.cols.length)
				{
					for (var coldetailid = 0;coldetailid<ct.p.cols[colid].coldetail.length;coldetailid++)
					{
						$topTableBody.append('<tr/>');
					}
				}
			}
			$('<colgroup width="20" span="'+(ct.p.rows.length-1)+'">').insertBefore($leftTableBody);
			$('<colgroup width="*">').insertBefore($leftTableBody);
			try
			{
				goDeep(ct.data[ct.p.jsonReader.root]);
			}
			catch (e)
			{
				opera.postError(e);
			}
			$leftTable.appendTo(ct.cross.leftdiv);
			$topTable.appendTo(ct.cross.topdiv);
			$contentTable.appendTo(ct.cross.contentdiv)
				.mousemove(function(e){
					if (e.ctrlKey)
					{
						ct.cross.contentdiv[0].scrollLeft += (e.clientX-ct.movingX)*(e.shiftKey?5:1);
						ct.cross.contentdiv[0].scrollTop += (e.clientY-ct.movingY)*(e.shiftKey?5:1);
					}
					ct.movingX = e.clientX;
					ct.movingY = e.clientY;
				});
		}
	});
}
})(jQuery);